using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using System.Linq;

namespace Gateway.Infrastructure.AutoDI;

/// <summary>
/// 自动注入扩展
/// </summary>
public static class AutoDiExtension
{
    /// <summary>
    /// 注入数据
    /// </summary>
    /// <param name="services"></param>
    /// <param name="type"></param>
    public static IServiceCollection AddDataService(this IServiceCollection services, Type type)
    {
        // #region 依赖注入
        var transientType = typeof(IDependency); //每次新建
        var singletonType = typeof(IDependencySingleton); //全局唯一
        //获取实现了接口IDenpendency和IDenpendcySingleton的程序集
        var assembly = type.Assembly;

        AddDataService(services, assembly);

        return services;
    }

    /// <summary>
    /// 注入数据
    /// </summary>
    /// <param name="services"></param>
    public static IServiceCollection AddDataService(this IServiceCollection services, Assembly assembly)
    {
        #region 依赖注入

        var transientType = typeof(IDependency); //每次新建
        var singletonType = typeof(IDependencySingleton); //全局唯一
        //获取实现了接口IDenpendency和IDenpendcySingleton的程序集

        var allTypes = assembly.GetTypes().Where(t =>
            t.GetInterfaces().Contains(transientType) || t.GetInterfaces().Contains(singletonType));
        //class的程序集
        var implementTypes = allTypes.Where(x => x.IsClass).ToArray();
        //接口的程序集
        var interfaceTypes = allTypes.Where(x => x.IsInterface).ToArray();
        foreach (var implementType in implementTypes)
        {
            var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
            //class有接口，用接口注入
            if (interfaceType != null)
            {
                //判断用什么方式注入
                if (interfaceType.GetInterfaces().Contains(transientType))
                {
                    services.AddScoped(interfaceType, implementType);
                }
                else if (interfaceType.GetInterfaces().Contains(singletonType))
                {
                    services.AddSingleton(interfaceType, implementType);
                }
            }
            else //class没有接口，直接注入class
            {
                //判断用什么方式注入
                if (implementType.GetInterfaces().Contains(transientType))
                {
                    services.AddScoped(implementType);
                }
                else if (implementType.GetInterfaces().Contains(singletonType))
                {
                    services.AddSingleton(implementType);
                }
            }
        }

        #endregion

        return services;
    }

    /// <summary>
    /// 注入数据
    /// </summary>
    /// <param name="services"></param>
    public static IServiceCollection AddDataService(this IServiceCollection services)
    {
        // #region 依赖注入
        var transientType = typeof(IDependency); //每次新建
        var singletonType = typeof(IDependencySingleton); //全局唯一
        //获取实现了接口IDenpendency和IDenpendcySingleton的程序集
        var assembiles = GetReferenceAssemblies();
        assembiles.ForEach(x => { AddDataService(services, x); });
        return services;
    }

    private static List<Assembly> GetReferenceAssemblies()
    {
        string[] paths = Directory.GetFiles(AppContext.BaseDirectory, "*.dll");
        // Assembly ass1;
        List<Assembly> assemblies = new List<Assembly>();
        foreach (var item in paths)
        {
            var ass = Assembly.LoadFrom(item);
            if (ass.FullName.StartsWith("Gateway") && !assemblies.Contains(ass))
            {
                assemblies.Add(ass);
            }
        }

        return assemblies;
    }

    private static void GetReferenceAssemblies(Assembly assembly, List<Assembly> list)
    {
        var assemblies = assembly.GetReferencedAssemblies().Where(x => x.FullName.StartsWith("Gateway"));
        foreach (var x in assemblies)
        {
            var ass = Assembly.Load(x);
            if (!list.Contains(ass))
            {
                list.Add(ass);
                GetReferenceAssemblies(ass, list);
            }
        }
    }
}